package com.wutong.resonance.model.tool;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程池工具类
 *
 * <p>
 * 该类提供了一个线程池的封装，用于执行异步任务。 线程池配置了核心线程数、最大线程数、空闲线程存活时间、任务队列和拒绝策略。 采用单例模式，保证全局只有一个线程池实例。
 * </p>
 *
 * @author wutong
 */
@Slf4j
public class ThreadPool implements ExecutorService {

    private final static int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();
    private final static int MAX_POOL_SIZE = 300;
    private final static int KEEP_ALIVE_TIME = 10;

    private static volatile ThreadPool ISNTANCE = null;

    private final ThreadPoolFactory threadPoolFactory = new ThreadPoolFactory();

    @Getter
    private final ExecutorService executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(5000),
            threadPoolFactory,
            new ThreadPoolExecutor.CallerRunsPolicy());

    private ThreadPool() {
    }

    public static int getMaximumPoolSize() {
        return MAX_POOL_SIZE;
    }

    public static int getCorePoolSize() {
        return CORE_POOL_SIZE;
    }

    public static int getKeepAliveTime() {
        return KEEP_ALIVE_TIME;
    }

    public static ThreadPool getInstance() {
        if (ISNTANCE == null) {
            synchronized (ThreadPool.class) {
                if (ISNTANCE == null) {
                    ISNTANCE = new ThreadPool();
                }
            }
        }
        return ISNTANCE;
    }

    /**
     * 获取指定编号的线程
     */
    public Thread getThread(int index) {
        return threadPoolFactory.getThread(index);
    }

    /**
     * 启动有序关闭，先前提交的任务将被执行，但不会接受新任务。 如果已经关闭，则调用没有其他效果。
     *
     * <p>此方法不等待先前提交的任务完成执行。使用 {@link #awaitTermination awaitTermination} 来完成此操作。</p>
     *
     * @throws SecurityException 如果存在安全管理器并且关闭此 ExecutorService 可能会操作调用者无权修改的线程， 因为它不持有
     *                           {@link RuntimePermission}{@code ("modifyThread")}， 或者安全管理器的 {@code checkAccess}
     *                           方法拒绝访问。
     */
    @Override
    public void shutdown() {
        executor.shutdown();
    }

    /**
     * 尝试停止所有正在执行的任务，停止等待任务的处理，并返回等待执行的任务列表。
     *
     * <p>此方法不等待正在执行的任务终止。使用 {@link #awaitTermination awaitTermination} 来完成此操作。</p>
     *
     * <p>除了尽最大努力尝试停止处理正在执行的任务之外，没有任何保证。
     * 例如，典型的实现将通过 {@link Thread#interrupt} 进行取消，因此任何无法响应中断的任务可能永远不会终止。</p>
     *
     * @return 从未开始执行的任务列表
     * @throws SecurityException 如果存在安全管理器并且关闭此 ExecutorService 可能会操作调用者无权修改的线程， 因为它不持有
     *                           {@link RuntimePermission}{@code ("modifyThread")}， 或者安全管理器的 {@code checkAccess}
     *                           方法拒绝访问。
     */
    @Override
    public List<Runnable> shutdownNow() {
        return executor.shutdownNow();
    }

    /**
     * 如果此执行程序已关闭，则返回 {@code true}。
     *
     * @return 如果此执行程序已关闭，则返回 {@code true}
     */
    @Override
    public boolean isShutdown() {
        return executor.isShutdown();
    }

    /**
     * 如果在关闭后所有任务都已完成，则返回 {@code true}。 请注意，除非先调用 {@code shutdown} 或 {@code shutdownNow}，否则 {@code isTerminated} 永远不会为
     * {@code true}。
     *
     * @return 如果在关闭后所有任务都已完成，则返回 {@code true}
     */
    @Override
    public boolean isTerminated() {
        return executor.isTerminated();
    }

    /**
     * 阻塞，直到在关闭请求后所有任务都已完成执行，或发生超时，或当前线程被中断，以先发生者为准。
     *
     * @param timeout 等待的最大时间
     * @param unit    超时参数的时间单位
     *
     * @return 如果此执行程序已终止，则返回 {@code true}；如果在终止之前超时已过，则返回 {@code false}
     * @throws InterruptedException 如果在等待时被中断
     */
    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return executor.awaitTermination(timeout, unit);
    }

    /**
     * 提交一个值返回任务以供执行，并返回一个 Future，表示任务的挂起结果。 Future 的 {@code get} 方法将在成功完成时返回任务的结果。
     *
     * <p>如果您想立即阻塞等待任务，可以使用以下形式的构造：{@code result = exec.submit(aCallable).get();}</p>
     *
     * <p>注意：{@link Executors} 类包含一组方法，可以将一些其他常见的类似闭包的对象（例如，{@link PrivilegedAction}）
     * 转换为 {@link Callable} 形式，以便可以提交它们。</p>
     *
     * @param task 要提交的任务
     *
     * @return 一个 Future，表示任务的挂起完成
     * @throws RejectedExecutionException 如果无法安排任务执行
     * @throws NullPointerException       如果任务为 null
     */
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return executor.submit(task);
    }

    /**
     * 提交一个 Runnable 任务以供执行，并返回一个 Future，表示该任务。 Future 的 {@code get} 方法将在成功完成时返回给定的结果。
     *
     * @param task   要提交的任务
     * @param result 要返回的结果
     *
     * @return 一个 Future，表示任务的挂起完成
     * @throws RejectedExecutionException 如果无法安排任务执行
     * @throws NullPointerException       如果任务为 null
     */
    @Override
    public <T> Future<T> submit(Runnable task, T result) {
        return executor.submit(task, result);
    }

    /**
     * 提交一个 Runnable 任务以供执行，并返回一个 Future，表示该任务。 Future 的 {@code get} 方法将在<em>成功</em> 完成时返回 {@code null}。
     *
     * @param task 要提交的任务
     *
     * @return 一个 Future，表示任务的挂起完成
     * @throws RejectedExecutionException 如果无法安排任务执行
     * @throws NullPointerException       如果任务为 null
     */
    @Override
    public Future<?> submit(Runnable task) {
        return executor.submit(task);
    }

    /**
     * 执行给定的任务，当所有任务都完成时，返回一个包含其状态和结果的 Future 列表。 {@link Future#isDone} 对于返回列表的每个元素都是 {@code true}。 请注意，<em>已完成</em>
     * 的任务可能已正常终止或通过抛出异常终止。 如果在操作进行时修改了给定的集合，则此方法的结果未定义。
     *
     * @param tasks 任务集合
     *
     * @return 一个 Future 列表，表示任务，其顺序与给定任务列表的迭代器产生的顺序相同，每个任务都已完成
     * @throws InterruptedException       如果在等待时被中断，在这种情况下，未完成的任务将被取消
     * @throws NullPointerException       如果任务或其任何元素为 {@code null}
     * @throws RejectedExecutionException 如果无法安排任何任务执行
     */
    @Override
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
        return executor.invokeAll(tasks);
    }

    /**
     * 执行给定的任务，当所有任务都完成或超时到期时（以先发生者为准），返回一个包含其状态和结果的 Future 列表。 {@link Future#isDone} 对于返回列表的每个元素都是 {@code true}。
     * 返回时，将取消尚未完成的任务。 请注意，<em>已完成</em> 的任务可能已正常终止或通过抛出异常终止。 如果在操作进行时修改了给定的集合，则此方法的结果未定义。
     *
     * @param tasks   任务集合
     * @param timeout 等待的最大时间
     * @param unit    超时参数的时间单位
     *
     * @return 一个 Future 列表，表示任务，其顺序与给定任务列表的迭代器产生的顺序相同。 如果操作未超时，则每个任务都将完成。如果超时，则其中一些任务将未完成。
     * @throws InterruptedException       如果在等待时被中断，在这种情况下，未完成的任务将被取消
     * @throws NullPointerException       如果任务、其任何元素或单位为 {@code null}
     * @throws RejectedExecutionException 如果无法安排任何任务执行
     */
    @Override
    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
        return executor.invokeAll(tasks, timeout, unit);
    }

    /**
     * 执行给定的任务，返回已成功完成的任务的结果（即，不抛出异常），如果有的话。 在正常或异常返回时，将取消尚未完成的任务。 如果在操作进行时修改了给定的集合，则此方法的结果未定义。
     *
     * @param tasks 任务集合
     *
     * @return 其中一个任务返回的结果
     * @throws InterruptedException       如果在等待时被中断
     * @throws NullPointerException       如果任务或任何要执行的元素任务为 {@code null}
     * @throws IllegalArgumentException   如果任务为空
     * @throws ExecutionException         如果没有任务成功完成
     * @throws RejectedExecutionException 如果无法安排任务执行
     */
    @Override
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
        return executor.invokeAny(tasks);
    }

    /**
     * 执行给定的任务，返回已成功完成的任务的结果（即，不抛出异常），如果有的话，在给定的超时时间过去之前。 在正常或异常返回时，将取消尚未完成的任务。 如果在操作进行时修改了给定的集合，则此方法的结果未定义。
     *
     * @param tasks   任务集合
     * @param timeout 等待的最大时间
     * @param unit    超时参数的时间单位
     *
     * @return 其中一个任务返回的结果
     * @throws InterruptedException       如果在等待时被中断
     * @throws NullPointerException       如果任务、单位或任何要执行的元素任务为 {@code null}
     * @throws TimeoutException           如果在任何任务成功完成之前给定的超时时间过去
     * @throws ExecutionException         如果没有任务成功完成
     * @throws RejectedExecutionException 如果无法安排任务执行
     */
    @Override
    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return executor.invokeAny(tasks, timeout, unit);
    }

    /**
     * 以一种有序的方式关机，此前提交的任务将被执行，但不会接受新任务。 此方法将一直等到所有任务完成执行并且执行器已终止。
     *
     * <p> 如果在此期间中断，此方法将停止所有正在执行的任务，就如同调用 {@link #shutdownNow()}。然后它继续等待，直到所有活动执行的任务都完成。
     * 等待执行的任务不会被执行。此方法返回前，中断状态将被重新确认。
     *
     * <p> 如果已经终止，则调用此方法没有任何效果。
     *
     * @throws SecurityException 如果存在安全管理器，并且关闭此 ExecutorService 可能会操纵调用者不允许修改的线程，因为它不持有
     *                           {@link RuntimePermission}{@code ("modifyThread") }，或者安全管理器的 {@code checkAccess}
     *                           方法拒绝访问。
     * @implSpec 默认实现调用 {@code shutdown()} 并等待任务使用 {@code awaitTermination} 完成执行。
     * @since 19
     */
    @Override
    public void close() {
        executor.close();
    }

    /**
     * 在将来的某个时间执行给定的命令。 该命令可以在新线程、池化线程或调用线程中执行，由 {@code Executor} 实现自行决定。
     *
     * @param command 要运行的可运行任务
     *
     * @throws RejectedExecutionException 如果无法接受此任务以供执行
     * @throws NullPointerException       如果 command 为 null
     */
    @Override
    public void execute(Runnable command) {
        executor.execute(command);
    }

    /**
     * 线程池工厂类
     *
     * <p>
     * 该类用于创建线程，并设置线程的名称、守护线程属性和优先级。
     * </p>
     */
    public static class ThreadPoolFactory implements ThreadFactory {

        /**
         * 自动递增的线程池编号，用于区分不同线程池
         * <p>
         * 线程池编号从1开始，每创建一个线程池，编号自增1
         * </p>
         */
        private final AtomicInteger threadNumber = new AtomicInteger(1);

        private final Map<Integer, Thread> threadMap = new ConcurrentHashMap<>();

        /**
         * 创建一个用于运行指定 runnable 的新未启动的 {@code Thread}。
         *
         * @param r 要由新线程实例执行的 runnable
         *
         * @return 构造的线程，或者如果创建线程请求被拒绝则返回 {@code null}
         * @see <a href="../../lang/Thread.html#inheritance">创建线程时的继承</a>
         */
        @Override
        public Thread newThread(Runnable r) {
            String theadName = "Resonance Thread - " + threadNumber.getAndIncrement();
            log.debug("创建线程: {}", theadName);
            Thread thread = new Thread(r, theadName);
            threadMap.put(threadNumber.get(), thread);
            if (!thread.isDaemon()) {
                thread.setDaemon(true);
            }
            if (thread.getPriority() != Thread.NORM_PRIORITY) {
                thread.setPriority(Thread.NORM_PRIORITY);
            }
            return thread;
        }


        public Thread getThread(int index) {
            return threadMap.get(index);
        }
    }
}